package com.mck.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mck.entity.Result;
import com.mck.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.security.SignatureException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

/**
 * @Author Max
 * @Date 2025/3/6 下午6:16
 * 用于处理JWT Token验证的拦截器
 */
@Component
@Slf4j
public class TokenInterceptor extends OncePerRequestFilter {

    private static final String AUTH_HEADER = "Authorization";
    private static final String BEARER_PREFIX = "Bearer ";

    @Override
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (HttpMethod.OPTIONS.matches(request.getMethod())) {
            filterChain.doFilter(request, response);
            return;
        }

        String uri = request.getRequestURI();
        log.info("拦截器触发 - 请求路径: {}", uri);
        // 白名单放行
        if (isWhiteListPath(uri)) {
            filterChain.doFilter(request, response);
            return;
        }

        String token = getTokenFromRequest(request);
        if (!StringUtils.hasText(token)) {
            // 使用 Result 返回错误信息
            sendJsonError(response, Result.error("凭证未提供"));
            return;
        }

        try {
            Jws<Claims> claimsJws = JwtUtil.parseToken(token);
            Claims claims = claimsJws.getPayload();

            // 设置用户ID到请求属性
            Object userIdObj = claims.get("userId");
            Integer userId = null;
            if (userIdObj instanceof Number) {
                userId = ((Number) userIdObj).intValue();
            }
            if (userId == null) {
                throw new RuntimeException("无效的用户ID");
            }
            request.setAttribute("userId", userId);

            // 用户类型有效性验证（1-5为有效类型）
            Object userTypeObj = claims.get("userTypeId");
            Integer userType = null;
            if (userTypeObj instanceof Number) {
                userType = ((Number) userTypeObj).intValue();
            }
            log.info("UserType from token: {}, claimsJws: {}", userType, claimsJws);
            if (userType == null || userType < 1 || userType > 5) {
                sendJsonError(response, Result.error("无效的用户类型"));
                return;
            }

            // 构建Spring Security权限对象
            List<GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + userType));

            // 设置安全上下文认证信息
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userId, null, authorities);
            ;
            SecurityContextHolder.getContext().setAuthentication(authentication);

            // 调试日志
            log.debug("Authentication set for user {} with roles: {}", claims.getSubject(), authorities);

            filterChain.doFilter(request, response);
        } catch (ExpiredJwtException e) {
            log.error("Token已过期: {}", e.getMessage());
            sendJsonError(response, Result.error("身份验证已过期"));
        } catch (JwtException | IllegalArgumentException e) {
            log.error("Token无效: {}", e.getMessage());
            sendJsonError(response, Result.error("身份验证无效"));
        }
    }

    /**
     * 统一发送错误响应（使用 Result 格式）
     */
    private void sendJsonError(HttpServletResponse response, Result result) throws IOException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 固定返回 401 状态码
        response.setContentType("application/json;charset=UTF-8");
        // 使用 Jackson 序列化 Result 对象
        ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(response.getWriter(), result);
    }

    /**
     * 从请求中获取token
     */
    private String getTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader(AUTH_HEADER);
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {
            return bearerToken.substring(BEARER_PREFIX.length());
        }
        return null;
    }

    /**
     * 判断是否为白名单路径（不需要验证token的路径）
     */
    private static final AntPathMatcher antPathMatcher = new AntPathMatcher();

    private boolean isWhiteListPath(String path) {

        // 登录、注册等接口不需要验证token
        return antPathMatcher.match("/api/public/login", path) || antPathMatcher.match("/static/**", path) || antPathMatcher.match("/favicon.ico", path) || antPathMatcher.match("/api/public/uploads/**", path);
    }

    /**
     * 从token中获取用户ID
     */
    public Integer getUserIdFromToken(String authHeader) {
        log.info("authHeader数据：{}", authHeader);
        if (!StringUtils.hasText(authHeader) || !authHeader.startsWith(BEARER_PREFIX)) {
            // log.error("null-1");
            return null;
        }

        String token = authHeader.substring(BEARER_PREFIX.length());
        try {
            Jws<Claims> claimsJws = JwtUtil.parseToken(token);
            Claims claims = claimsJws.getPayload();
            log.info("claims: {}", claims);
            Object userIdValue = claims.get("userId");
            if (userIdValue != null) {
                log.info("claims中存在的userId: {}", userIdValue);
                if (userIdValue instanceof Number) {
                    return ((Number) userIdValue).intValue();
                } else {
                    log.error("userId类型错误，不是数值类型: {}", userIdValue.getClass());
                    return null;
                }
            }
        } catch (Exception e) {
            log.error("token错误: {}", e.getMessage());
        }
        // log.error("null-2");
        return null;
    }
}
